// Package service
// Created by GoLand
// @User: lenora
// @Date: 2024/1/18
// @Time: 12:41

package service

import (
	"camp/microbook/internal/domain"
	"camp/microbook/internal/events/article"
	"camp/microbook/internal/repository"
	"camp/microbook/pkg/logger"
	"context"
	"time"
)

//go:generate mockgen -source=./article.go -package=svcmocks -destination=./mocks/article_gen.go
type ArticleService interface {
	Save(ctx context.Context, article domain.Article) (uint64, error)
	Withdraw(ctx context.Context, uid, id uint64) error
	GetByAuthor(ctx context.Context, uid uint64, offset, limit int) ([]domain.Article, error)
	GetById(ctx context.Context, id uint64) (domain.Article, error)
	GetPubById(ctx context.Context, id, userId uint64) (domain.Article, error)
	ListByOffset(ctx context.Context, start time.Time, offset, size int) ([]domain.Article, error)
}

func NewArticleService(artRepo repository.ArticleRepository, producer article.Producer, logger2 logger.Logger) ArticleService {
	return &articleService{
		repo:     artRepo,
		producer: producer,
		l:        logger2,
	}
}

func NewArticleServiceV1(readerRepo repository.ArticleReaderRepository, authorRepo repository.ArticleAuthorRepository) ArticleService {
	return &articleService{
		readerRepo: readerRepo,
		authorRepo: authorRepo,
	}
}

type articleService struct {
	repo repository.ArticleRepository

	readerRepo repository.ArticleReaderRepository
	authorRepo repository.ArticleAuthorRepository

	producer article.Producer
	l        logger.Logger
}

func (svc *articleService) ListByOffset(ctx context.Context, start time.Time, offset, size int) ([]domain.Article, error) {
	return svc.repo.ListByOffset(ctx, start, offset, size)
}

func (svc *articleService) GetPubById(ctx context.Context, id, userId uint64) (domain.Article, error) {
	res, err := svc.repo.GetByPubId(ctx, id)
	if err != nil {
		return domain.Article{}, err
	}

	go func() {
		if err := svc.producer.ReadEvent(article.ReadEvent{
			UserId:    userId,
			ArticleId: id,
		}); err != nil {
			svc.l.Error("send read event failed",
				logger.Error(err),
				logger.Uint64("article id", id),
				logger.Uint64("user id", userId),
			)
		}
	}()

	return res, nil
}

func (svc *articleService) GetById(ctx context.Context, id uint64) (domain.Article, error) {
	return svc.repo.GetById(ctx, id)
}

func (svc *articleService) GetByAuthor(ctx context.Context, uid uint64, offset, limit int) ([]domain.Article, error) {
	if limit == 0 {
		limit = 100
	}
	return svc.repo.GetByAuthor(ctx, uid, offset, limit)
}

func (svc *articleService) Withdraw(ctx context.Context, uid, id uint64) error {
	return svc.repo.SyncStatus(ctx, uid, id, domain.StatusPrivate)
}

func (svc *articleService) Save(ctx context.Context, article domain.Article) (uint64, error) {
	article.Status = domain.StatusUnpublished
	if article.Id > 0 {
		err := svc.repo.Edit(ctx, article)
		return article.Id, err
	} else {
		return svc.repo.Create(ctx, article)
	}
}

func (svc *articleService) Publish(ctx context.Context, article domain.Article) (uint64, error) {
	article.Status = domain.StatusPublished
	return svc.repo.Sync(ctx, article)
}

func (svc *articleService) PublishV1(ctx context.Context, article domain.Article) (uint64, error) {
	var (
		id  = article.Id
		err error
	)
	article.Status = domain.StatusPublished
	if article.Id > 0 {
		err = svc.authorRepo.Update(ctx, article)
	} else {
		id, err = svc.authorRepo.Create(ctx, article)
	}
	if err != nil {
		return 0, err
	}
	article.Id = id
	return id, svc.readerRepo.Create(ctx, article)
}
